/*
 * @Author: yangni 920556271@qq.com
 * @Date: 2024-01-09 10:11:17
 * @LastEditors: yangni 920556271@qq.com
 * @LastEditTime: 2024-05-16 10:42:37
 * @FilePath: /yt-devops/lib/pages/devices/device_service/device_controll_utils.dart
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import 'package:bruno/bruno.dart';
import 'package:expressions/expressions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:progress_loading_button/progress_loading_button.dart';
import 'package:yt_devops/components/gaps.dart';
import 'package:yt_devops/components/log_utils.dart';
import 'package:yt_devops/configs.dart';
import 'package:yt_devops/model/data_type_config.dart';
import 'package:yt_devops/model/device_obj/prompt_rule_model.dart';
import 'package:yt_devops/model/device_obj/properties_model.dart';
import 'package:yt_devops/model/device_obj/service_config_model.dart';
import 'package:yt_devops/model/device_obj/spec/arrays_spec.dart';
import 'package:yt_devops/model/device_obj/spec/double_spec.dart';
import 'package:yt_devops/model/device_obj/spec/struct_spec.dart';
import 'package:yt_devops/resources/resources.dart';
import 'package:yt_devops/resources/str_res_keys.dart';
import 'package:yt_devops/services/device/device_sevices.dart';
import 'package:yt_devops/styles/styles.dart';
import 'package:yt_devops/utils/enums.dart';
import 'package:yt_devops/utils/extensions.dart';
import 'package:yt_devops/utils/func_utils.dart';
import 'package:yt_devops/utils/toast_utils.dart';

class DeviceControllUtils {
  /// 确认弹窗
  static Future showConfirmDialog(
      BuildContext context,
      List<PromptRuleModel>? ruleModel,
      PropertiesModel model,
      dynamic oldValue,
      dynamic newValue,
      {Function? onConfirm,
      Function? onCancel}) async {
    if (ruleModel == null || FuncUtils.isListEmpty(ruleModel)) {
      onConfirm?.call();
      return;
    }
    var promptRule = ruleModel.removeAt(0);

    TextSpan richTextContent = getRichText(
        text: promptRule.content ?? '',
        name: model.name ?? '',
        oldValue: oldValue,
        newValue: newValue,
        model: model.dataType);
    BrnDialogManager.showConfirmDialog(
      context,
      title: ResKey.info.tr,
      cancel: ResKey.cancel.tr,
      confirm: ResKey.sure.tr,
      messageWidget: Container(
        alignment: Alignment.center,
        padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8),
        child: RichText(text: richTextContent),
      ),
      barrierDismissible: false,
      themeData: BrnDialogConfig(
        contentTextStyle: BrnTextStyle(
          color: Colour.mainTextColor,
          fontSize: 18.sp,
          fontWeight: FontWeight.w500,
        ),
        contentPaddingSm: EdgeInsets.symmetric(
          horizontal: 42.w,
        ),
        contentPaddingLg: EdgeInsets.symmetric(
          horizontal: 42.w,
        ),
      ),
      onConfirm: () async {
        Get.back();
        showConfirmDialog(context, ruleModel, model, oldValue, newValue,
            onConfirm: onConfirm, onCancel: onCancel);
      },
      onCancel: () {
        onCancel?.call();
        Get.back();
      },
    );
  }

  static Widget createHead(String title,
      {String? tip,
      bool isShowSetButton = true,
      bool isShowRefreshButton = false,
      bool buttonDisable = false,
      Function? taphandler,
      Future Function()? refreshHandler}) {
    return Container(
      alignment: Alignment.centerLeft,
      margin: EdgeInsets.only(top: 10.h),
      padding: EdgeInsets.only(left: 16.w, right: 8.w, top: 14, bottom: 14),
      decoration: const BoxDecoration(
        color: Colour.white,
        border: Border(
          bottom: BorderSide(color: Colour.dividerColor, width: 0.5),
        ),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Expanded(
                child: Text(
                  title,
                  style: TextStyle(
                      color: Colour.mainTextColor,
                      fontWeight: FontWeight.bold,
                      fontSize: 18.sp),
                ),
              ),
              if (isShowSetButton)
                Padding(
                  padding: EdgeInsets.only(right: 6.w),
                  child: GestureDetector(
                    onTap: () {
                      if (!buttonDisable) {
                        taphandler?.call();
                      }
                    },
                    child: Text(
                      ResKey.deviceParameterSetting.tr,
                      style: TextStyle(
                        color: buttonDisable
                            ? Colour.lightTextColor
                            : Colour.primaryColor,
                        fontWeight: FontWeight.bold,
                        fontSize: 16.sp,
                      ),
                    ),
                  ),
                ),
              if (isShowRefreshButton)
                LoadingButton(
                    height: 25,
                    type: LoadingButtonType.Text,
                    defaultWidget: Row(
                      children: [
                        Image.asset(
                          ImageAssets.iconServiceRefesh,
                          width: 16.w,
                          height: 16.w,
                        ),
                        Gaps.hGap4,
                        Text(
                          ResKey.updateDevice.tr,
                          style: TextStyles.pf14,
                        )
                      ],
                    ),
                    onPressed: refreshHandler)
            ],
          ),
          if (!FuncUtils.isEmpty(tip))
            Padding(
              padding: EdgeInsets.only(top: 2.h),
              child: Text(
                '($tip)',
                style: TextStyles.lf12,
              ),
            )
        ],
      ),
    );
  }

  static bool isShowType(DeviceModelShowType? showType) {
    return showType != null || showType != DeviceModelShowType.others;
  }

  /// [model] 的 属性 [disabled] 不为空则执行
  static bool isDisabledByExpression(
      PropertiesModel? model, Map<String, dynamic> dataSource) {
    bool disabled = false;

    if (model != null && !FuncUtils.isEmpty(model.disbaled)) {
      /// 兼容$data跟data的情况
      var disbleExpress = model.disbaled!.replaceAllMapped('\$', (match) => '');

      Expression expression = Expression.parse(disbleExpress);
      var dataMap = {};
      dataSource.forEach((key, value) {
        var realId = '';

        /// 移除设备id前缀
        var arr = key.split('-');
        if (arr.length == 2) {
          realId = arr[0];
        } else {
          realId = key;
        }

        dataMap.putIfAbsent(realId, () => value);
      });

      var context = {"data": dataMap};
      var evaluator = const ExpressionEvaluator();

      disabled = evaluator.eval(expression, context);
    }

    return disabled;
  }

  static String? disableTip(String? tip, bool disabled) {
    return !FuncUtils.isEmpty(tip) && disabled ? tip! : null;
  }

  // 富文本

  static TextSpan getRichText({
    required String text,
    required String name,
    required dynamic oldValue,
    required dynamic newValue,
    required DataTypeConfig? model,
  }) {
    List<InlineSpan> textSapns = [];
    RegExp expString = RegExp(r"\${.*?}");
    var newValueFormat = FuncUtils.formatModelValue(newValue, model);
    var oldValueFormat = FuncUtils.formatModelValue(oldValue, model);

    //正则表达式是否在字符串[input]中有匹配。
    if (expString.hasMatch(text)) {
      Iterable<RegExpMatch> matches = expString.allMatches(text);

      int index = 0;
      int count = 0;
      for (var matche in matches) {
        count++;
        String c = text.substring(matche.start, matche.end);
        //匹配到的东西,在首位
        if (index == matche.start) {
          index = matche.end;
        }
        //匹配到的东西,不在首位
        else if (index < matche.start) {
          String leftStr = text.substring(index, matche.start);
          index = matche.end;
          textSapns.add(
            TextSpan(
              text: leftStr,
              style: TextStyles.f14,
            ),
          );
        }

        Expression expression = Expression.parse(getInsertValue(c) ?? '');
        var context = {
          'oldValue': oldValue,
          'newValue': newValue,
          'oldValueFormat': oldValueFormat,
          'newValueFormat': newValueFormat,
          'name': name
        };
        var evaluator = const ExpressionEvaluator();

        textSapns.add(
          TextSpan(
              text: evaluator.eval(expression, context),
              style: TextStyles.pf14),
        );
        //是否是最后一个符合,并且后面是否有字符串
        if (matches.length == count && text.length > index) {
          String rightStr = text.substring(index, text.length);
          textSapns.add(
            TextSpan(
              text: rightStr,
              style: TextStyles.f14,
            ),
          );
        }
      }
    } else {
      textSapns.add(
        TextSpan(
          text: text,
          style: TextStyles.f14,
        ),
      );
    }

    return TextSpan(children: textSapns);
  }

  /// 按照其物模型返回创建对应结构的空数据
  static dynamic createNewDataForList(DataTypeConfig itemDataType) {
    switch (itemDataType.type) {
      case DataTypeConfigEnum.struct:
        List<PropertiesModel> propertis =
            (itemDataType.specs as List<PropertiesModel>?) ?? [];
        Map dataMap = {};
        for (var e in propertis) {
          dataMap.putIfAbsent(e.id, () => '');
        }
        return dataMap;

      case DataTypeConfigEnum.array:
        var list =
            createNewDataForList((itemDataType.specs as ArraysSpec).item!);

        return list;

      default:
        PropertiesModel? property = itemDataType.specs as PropertiesModel?;
        if (property != null) {
          return {}.putIfAbsent(property.id, () => '');
        }
    }
  }

  static String getPropertyId(PropertiesModel property) {
    var realProertyId = getPropertyIdWithoutProduct(property.id);

    if (property.deviceId == null) {
      return realProertyId;
    } else {
      var text = "$realProertyId-${property.deviceId}";

      return text;
    }
  }

  /// 移除掉跨物模型的Property的product缩写
  /// ```dart
  /// b.enabledStatus ///移除前
  /// enabledStatus  /// 移除后
  /// ```
  ///
  static String getPropertyIdWithoutProduct(String propertyId) => propertyId
      .replaceFirst(RegExp(r'^(.*?)(?=\.)'), "")
      .replaceFirst('.', '');

  ///
  /// 计算所选时间和已选的时间段是否有重合
  /// 这里的时间段格式是 "09:33-14:50"
  ///
  static bool? isTimeOverLap(List<String> timeFrameList, String time) {
    if (time.isEmpty || !time.contains('-')) {
      return null;
    }
    var timeList = time.split('-');
    String start1 = timeList[0];
    String end1 = timeList[1];
    for (int i = 0; i < timeFrameList.length; i++) {
      if (timeFrameList[i].isEmpty || !timeFrameList[i].contains('-')) {
        continue;
      }
      var times = timeFrameList[i].split('-');
      String start2 = times[0];
      String end2 = times[1];

      if ((timeFrameToMinut(start1) ?? 0) < (timeFrameToMinut(end2) ?? 0) &&
          (timeFrameToMinut(end1) ?? 0) > (timeFrameToMinut(start2) ?? 0)) {
        return true;
      }
    }

    return false;
  }

  static int? timeFrameToMinut(String time) {
    if (time.isEmpty || !time.contains(':')) {
      return null;
    }
    var timeList = time.split(':');
    return int.parse(timeList[0]) * 60 + int.parse(timeList[1]);
  }

  /// 下发指令
  static Future remoteSetting(
      {required BuildContext context,
      required int deviceId,
      required ServiceConfigModel service,
      PropertiesModel? property,
      required Map<String, dynamic> data,
      // 是否loading弹窗
      bool showLoading = true,

      ///确认回调
      Function(bool success, {Map<String, dynamic>? data})?
          confirmHandler}) async {
    await Configs.httpClient.requestWithLoading(() async {
      var inputParamMap = {
        ...(service.extraParams ?? {}),
      };

      /// 移除前缀
      data.forEach((key, value) {
        var realId = '';

        /// 移除产品前缀
        var devicePropertyId = getPropertyIdWithoutProduct(key);

        /// 移除设备id前缀
        var arr = devicePropertyId.split('-');
        if (arr.length == 2) {
          realId = arr[0];
        } else {
          realId = devicePropertyId;
        }

        inputParamMap.putIfAbsent(realId, () => value);
      });

      Map<String, dynamic> params = {
        'deviceId': property?.deviceId ?? service.deviceId ?? deviceId,
        'serviceId': service.id,
        'input': inputParamMap,
      };
      await DeviceServices().deviceRemoteSetting(params);
      FuncUtils.requestTipsDislog(Get.context!, ResKey.executeSuccess.tr, true,
          duration: const Duration(
            milliseconds: 1000,
          )).then((value) => confirmHandler?.call(true, data: data));
    }, onError: (err) {
      /// 下发失败
      FuncUtils.requestTipsDislog(
        context,
        ResKey.executeFailure.tr,
        false,
        duration: const Duration(
          milliseconds: 1000,
        ),
      ).then((value) => confirmHandler?.call(false));

      return true;
    }, showLoading: showLoading);
  }

  /// 验证是否所有参数已填
  static bool validate(
      List<PropertiesModel> propertyList, Map<String, dynamic> data) {
    if (data.isEmpty) {
      TotastUtils.tip(Get.context!, title: '请输入要下发的参数');
      return false;
    }
    for (var output in propertyList) {
      if (!isPropertyRequired(output)) {
        continue;
      }
      var key = getPropertyId(output);
      var value = data[key];

      if (value is List) {
        for (var item in value) {
          if (item is Map) {
            for (var k in item.keys) {
              if (item[k] == null || item[k] == '' || item[k] == '-') {
                var propertyList = (output.dataType?.specs as ArraysSpec)
                    .item
                    ?.specs as List<PropertiesModel>;
                var idx = propertyList.indexWhere((element) => element.id == k);
                if (idx > -1) {
                  TotastUtils.tip(Get.context!,
                      title: '${ResKey.plsInput.tr}${propertyList[idx].name}');
                  return false;
                }
              }
            }
          }
        }
      } else if (value is Map) {
        for (var k in value.keys) {
          if (value[k] == null) {
            var struct = output.dataType?.specs as StructSpec;
            var idx = struct.specs!.indexWhere((element) => element.id == k);
            if (idx > -1) {
              TotastUtils.tip(Get.context!,
                  title: '${ResKey.plsInput.tr}${struct.specs![idx].name}');
              return false;
            }
          }
        }
      } else {
        if (value == null &&
            output.dataType?.type != DataTypeConfigEnum.array) {
          TotastUtils.tip(Get.context!,
              title: '${ResKey.plsInput.tr}${output.name}');
          return false;
        } else if (output.dataType?.specs is DoubleSpec) {
          var spec = output.dataType?.specs as DoubleSpec;
          var max = spec.max ?? 9999999999999, min = spec.min ?? 0;
          var numberValue;
          if (value is String) {
            numberValue = num.tryParse(value);
          } else {
            numberValue = value;
          }
          if (numberValue != null && (numberValue > max)) {
            TotastUtils.tip(Get.context!, title: '${output.name}的值不能大于$max');
            return false;
          } else if (numberValue != null && (numberValue < min)) {
            TotastUtils.tip(Get.context!, title: '${output.name}的值不能小于$min');
            return false;
          }
        }
      }
    }
    return true;
  }

  // 从页面配置获取所有绑定设备的Id
  static List<int> getDeviceIdFromTempl(list) {
    List<int> deviceIds = FuncUtils.getPropFromTree<dynamic, int>(
      list,
      getKey: (item) => item['deviceId'],
      getChildren: (item) => item['children'],
    );
    return deviceIds.toSet().toList();
  }

  static bool isPropertyRequired(PropertiesModel property) {
    if (property.required == false) {
      return false;
    }
    return true;
  }

  static List<PromptRuleModel>? getPromptRuleList(
      ServiceConfigModel serviceConfigModel, PropertiesModel model) {
    var list = serviceConfigModel.promptRule ?? model.promptRule;
    return list
        ?.map<PromptRuleModel>((e) => PromptRuleModel.fromJson(e.toJson()))
        .toList();
  }

  /**
   * 获取${}中间的字符串
   */
  static String? getInsertValue(String str) {
    RegExp exp = RegExp(r'\$\{([^}]+)\}');
    RegExpMatch? matche = exp.firstMatch(str);
    return matche?.group(1);
  }
}
